#include "md5.h"
#include <stdio.h>
#include <string.h>
#include <errno.h>

static const uint32_t k[64] = {
    0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee,
    0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
    0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be,
    0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821,
    0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa,
    0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,
    0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed,
    0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a,
    0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c,
    0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
    0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05,
    0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,
    0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039,
    0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,
    0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1,
    0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391
};

static const uint32_t r[] = {
    7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22,
    5,  9, 14, 20, 5,  9, 14, 20, 5,  9, 14, 20, 5,  9, 14, 20,
    4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23,
    6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21
};

#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))

#define F(x,y,z) (((x) & (y)) | ((~x) & (z)))
#define G(x,y,z) (((x) & (z)) | ((y) & (~z)))
#define H(x,y,z) ((x) ^ (y) ^ (z))
#define I(x,y,z) ((y) ^ ((x) | (~z)))

static void MD5Transform(uint32_t state[4], uint8_t block[64]) {
    uint32_t a = state[0], b = state[1], c = state[2], d = state[3];
    uint32_t x[16];

    for (int i = 0; i < 16; i++) {
        x[i] = (uint32_t)block[i*4] |
                ((uint32_t)block[i*4+1] << 8) |
                ((uint32_t)block[i*4+2] << 16) |
                ((uint32_t)block[i*4+3] << 24);
    }

    for (int i = 0; i < 64; i++) {
        uint32_t f, g;

        if (i < 16) {
            f = F(b, c, d);
            g = i;
        } else if (i < 32) {
            f = G(b, c, d);
            g = (5*i + 1) % 16;
        } else if (i < 48) {
            f = H(b, c, d);
            g = (3*i + 5) % 16;
        } else {
            f = I(b, c, d);
            g = (7*i) % 16;
        }

        f = f + a + k[i] + x[g];
        a = d;
        d = c;
        c = b;
        b = b + ROTATE_LEFT(f, r[i]);
    }

    state[0] += a;
    state[1] += b;
    state[2] += c;
    state[3] += d;
}

static void MD5Init(MD5Context *ctx) {
    ctx->state[0] = 0x67452301;
    ctx->state[1] = 0xEFCDAB89;
    ctx->state[2] = 0x98BADCFE;
    ctx->state[3] = 0x10325476;
    ctx->bitcount = 0;
}

static void MD5Update(MD5Context *ctx, uint8_t *input, size_t inputlen) {
    size_t i, index, partlen;

    index = (ctx->bitcount >> 3) & 0x3F;
    ctx->bitcount += inputlen << 3;
    partlen = 64 - index;

    if (inputlen >= partlen) {
        memcpy(&ctx->buffer[index], input, partlen);
        MD5Transform(ctx->state, ctx->buffer);

        for (i = partlen; i + 63 < inputlen; i += 64) {
            MD5Transform(ctx->state, &input[i]);
        }
        index = 0;
    } else {
        i = 0;
    }

    memcpy(&ctx->buffer[index], &input[i], inputlen - i);
}

static void MD5Final(uint8_t digest[16], MD5Context *ctx) {
    uint8_t bits[8];
    size_t index, padlen;

    for (int i = 0; i < 8; i++) {
        bits[i] = (ctx->bitcount >> (i * 8)) & 0xFF;
    }

    index = (ctx->bitcount >> 3) & 0x3F;
    padlen = (index < 56) ? (56 - index) : (120 - index);
    uint8_t padding[64];
    padding[0] = 0x80;
    for (int i = 1; i < 64; i++) {
        padding[i] = 0;
    }

    MD5Update(ctx, padding, padlen);
    MD5Update(ctx, bits, 8);

    for (int i = 0; i < 16; i++) {
        digest[i] = (ctx->state[i >> 2] >> ((i & 3) << 3)) & 0xFF;
    }
}

int calculate_md5(const char *filepath, char *hash_output) {
    FILE *file = fopen(filepath, "rb");
    if (!file) {
        fprintf(stderr, "错误: 无法打开文件 %s: %s\n", filepath, strerror(errno));
        return -1;
    }

    // 检查文件大小
    if (fseek(file, 0, SEEK_END) != 0) {
        fprintf(stderr, "错误: 无法获取文件大小 %s: %s\n", filepath, strerror(errno));
        fclose(file);
        return -1;
    }

    long file_size = ftell(file);
    if (file_size < 0) {
        fprintf(stderr, "错误: 文件大小无效 %s: %s\n", filepath, strerror(errno));
        fclose(file);
        return -1;
    }

    rewind(file);

    MD5Context ctx;
    MD5Init(&ctx);

    // 使用64KB缓冲区，减少每次读取的时间，提高中断响应速度
    uint8_t buffer[64 * 1024];
    size_t bytes_read;
    size_t total_read = 0;
    size_t chunks_since_last_check = 0;

    while ((bytes_read = fread(buffer, 1, sizeof(buffer), file)) > 0) {
        // 每读取4个块检查一次中断信号，平衡性能和响应速度
        if (++chunks_since_last_check >= 4) {
            chunks_since_last_check = 0;
            if (g_shutdown_requested) {
                fclose(file);
                return -2;  // 返回-2表示被中断
            }
        }

        MD5Update(&ctx, buffer, bytes_read);
        total_read += bytes_read;

        // 检查读取错误
        if (ferror(file)) {
            fprintf(stderr, "错误: 读取文件失败 %s: %s\n", filepath, strerror(errno));
            fclose(file);
            return -1;
        }
    }

    fclose(file);

    // 验证是否读取了整个文件
    if (total_read != (size_t)file_size) {
        fprintf(stderr, "警告: 文件读取不完整 %s (期望: %ld, 实际: %zu)\n",
                filepath, file_size, total_read);
    }

    uint8_t digest[16];
    MD5Final(digest, &ctx);

    for (int i = 0; i < 16; i++) {
        sprintf(hash_output + (i * 2), "%02x", digest[i]);
    }
    hash_output[32] = '\0';

    return 0;
}
